/**
 * 
 * @Project HRL Simulation
 * @Date 20.06.2011
 * @Author Aebi Pascal, Kocher Marc, Sriranjan George
 * @School BFH-TI Biel, Projekt 1
 *
 */

package strategy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import utils.AgendaEntry;
import utils.Position;
import utils.Prop;
import utils.Resource;
import elements.Buffer;
import elements.Conveyor;
import elements.Lift;
import elements.Load;
import elements.Rack;
import exceptions.ResourceException;


public abstract class AbstractStrategy implements Strategy {

	/** The constants for the speeds of lifts and conveyor. */
	protected final int LIFT_SPEED = Prop.getInt(Prop.LIFT_SPEED);
	protected final int CONVEYOR_SPEED = Prop.getInt(Prop.CONVEYOR_SPEED);
	/** The constants for the dimensions of the loads. */
	protected final int RACK_LENGTH = Prop.getInt(Prop.RACK_LENGTH);
	protected final int RACK_HEIGHT = Prop.getInt(Prop.RACK_HEIGHT);
	/** The racks. */
	protected List<Rack> racks;
	/** The buffers. */
	protected List<Buffer> buffers;
	/** The lifts. */
	protected List<Lift> lifts;
	/** The available conveyor resources. */
	protected List<Resource> availableConveyorResources;
	/** The available input buffers. */
	protected List<Buffer> availableInputBuffers;
	/** The conveyor. */
	protected Conveyor conveyor;
	/** The entry buffer. */
	protected Buffer entryBuffer;
	/** The exit buffer. */
	protected Buffer exitBuffer;
	/** The current longest conveyor route. */
	protected int maxConveyorRoute = 0;
	/** The system entry time . */
	protected int systemEntryTime = 0;
	/** The lift. */
	protected Lift lift = null;
	/** The input buffer. */
	protected Buffer inputBuffer;
	/** The output buffer. */
	protected Buffer outputBuffer;
	/** The rack resource. */
	protected Resource rackResource;
	/** The load. */
	protected Load load;
	
	
	/**
	 * Calculates the route from a given start resource to a given end resource
	 * 
	 * @param from - start resource
	 * @param to - end resource
	 * @return route - the way from the start resource to the end resource
	 * @throws ResourceException not possible Resource
	 */
	protected int calculateRouteFromTo(Resource from, Resource to) throws ResourceException{
		int route=0,deltaX=0,deltaY=0,deltaZ=0;
		int fromX=from.getPosition().getX();
		int fromY=from.getPosition().getY();
		int fromZ=from.getPosition().getZ();
		int toX=to.getPosition().getX();
		int toY=to.getPosition().getY();
		int toZ=to.getPosition().getZ();
		
		/**
		 * from and to are the same resource 
		 */
		if(from.equals(to)){
			return 1;
		}
		
		/**
		 * from a buffer to any resource
		 */
		else if(from instanceof Buffer){
			// input buffer
			if(((Buffer) from).getType() == Buffer.Type.IN){
				// input buffer -> lift
				if(to instanceof Lift){
					if(Math.abs(fromX - toX) == 1){
						return 1;
					}
					else{
						throw new ResourceException("Calulate route error. Not possible lift"+" \nfrom:"+from+" \nto"+to);
					}
				}
				// entry buffer -> input buffer or output buffer
				else if(to instanceof Buffer){
					deltaX = toX - fromX;
					deltaY = toY - fromY;
					if(deltaY<=0){
						route = Math.abs(deltaX)+Math.abs(deltaY) + 2; //+1 because it starts on zero, +1 because enter/exit system
					}else{
						route = Math.abs(deltaX)+Math.abs(deltaY);
						route = this.maxConveyorRoute - route + 2; //+1 because it starts on zero, +1 because enter/exit system
					}
					return route;
			    }
				// input buffer -> conveyor resource
				else if(this.conveyor.getResources().contains(to)){
					// entry buffer -> first conveyor resoure
					if(Math.abs(fromX - toX) == 1 && this.conveyor.getResources().contains(to)){
						return 1;
					}
					// entry buffer -> conveyor resource
					else{
						deltaX = toX - fromX;
						deltaY = toY - fromY;
						// upper part
						if(deltaY<0){
							route = Math.abs(deltaX)+Math.abs(deltaY);
						}
						// lower part
						else{
							route = Math.abs(deltaX)+Math.abs(deltaY);
							route = this.maxConveyorRoute - route + 2; //+1 because it starts on zero, +1 because enter/exit system
						}
						return route;
				    }
				}
				// input buffer -> corner positions
				else if(this.conveyor.getCornerPositions().contains(to.getPosition())){
					deltaX = toX - fromX;
					deltaY = toY - fromY;
					// upper part
					if(deltaY<0){
						route = Math.abs(deltaX)+Math.abs(deltaY);
					}
					// lower part
					else{
						route = Math.abs(deltaX)+Math.abs(deltaY);
						route = this.maxConveyorRoute - route + 2; //+1 because it starts on zero, +1 because enter/exit system
					}
					return route;
				}
				
			}
			// output buffer
			else if(((Buffer) from).getType() == Buffer.Type.OUT){
				// output buffers -> exit buffer
				if(to instanceof Buffer){
					if(((Buffer) to).getType() == Buffer.Type.OUT){
						Buffer fromBuffer = (Buffer)from;
						Buffer toBuffer = (Buffer)to;
						if(fromBuffer.getType() == Buffer.Type.OUT && toBuffer.getType() == Buffer.Type.OUT){
							deltaX = toX - fromX;
							deltaY = toY - fromY;
							if(fromBuffer.getPosition().getY() == 1){
								return this.maxConveyorRoute - (Math.abs(deltaX)+Math.abs(deltaY))+2; //+1 because it starts on zero, +1 because enter/exit system
							}
							else{
								return (Math.abs(deltaX)+Math.abs(deltaY))+2; //+1 because it starts on zero, +1 because enter/exit system
							}
						}
					}
				}
				// output buffers -> conveyor resources
				else if(this.conveyor.getResources().contains(to)){
					deltaX = toX - fromX;
					deltaY = toY - fromY;
//					// from output buffer to conveyor resource next to
					// upper part
					if(fromY == 1){
						if(toY == 0){
							return Math.abs(deltaX) + Math.abs(deltaY);
						}
						else{
							int toUpperRightCornerX = this.conveyor.getCornerPositions().get(1).getX();
							int toUpperRightCornerY = this.conveyor.getCornerPositions().get(1).getY();
							deltaX = toUpperRightCornerX - fromX;
							deltaY = toUpperRightCornerY - fromY;
							route = Math.abs(deltaX)+Math.abs(deltaY);
							deltaX = toX - toUpperRightCornerX;
							deltaY = toY - toUpperRightCornerY;
							return route + Math.abs(deltaX)+Math.abs(deltaY);
						}
					}
					// lower part
					else{
						return Math.abs(deltaX) + Math.abs(deltaY);
					}
				}
				// output buffer -> corner positions
				else if(this.conveyor.getCornerPositions().contains(to.getPosition())){
					deltaX = toX - fromX;
					deltaY = toY - fromY;
					if(fromY > (this.RACK_LENGTH + 4)/2 && toX<fromX){
						route = Math.abs(deltaX)+Math.abs(deltaY);
					}
					else if(toX > fromX && fromY == 1 || toX < fromX && fromY > (this.RACK_LENGTH + 4)/2){
						route = Math.abs(deltaX)+Math.abs(deltaY);
						if(fromY < toY){
							route += 2;
						}
					}
					else{
						route = Math.abs(deltaX)+Math.abs(deltaY);
						route = this.maxConveyorRoute - route;
					}
					return route;
				}
			}
		}
		
		/**
		 * from a lift to any resource
		 */	
		else if(from instanceof Lift){
			Position lastLiftPos = from.getAgenda().getLastEntry().getEndPos();
			// at first time there is no agenda entry in the lift, so get default position
			if(lastLiftPos == null){
				lastLiftPos = from.getPosition();
			}
			fromX = lastLiftPos.getX();
			fromY = lastLiftPos.getY();
			fromZ = lastLiftPos.getZ();
			// lift -> output buffer
			if(to instanceof Buffer){
				if(((Buffer)to).getType() == Buffer.Type.OUT && Math.abs(fromX - toX) == 1 && Math.abs(fromY - toY) == 0){
					return 1;
				}
				// lift -> input or output buffer  (lift -> lift, pythagoras)
				else if(Math.abs(fromX - toX) == 1 && Math.abs(fromY - toY) != 0){
					deltaY = toY - fromY;
					deltaZ = toZ - fromZ;
					return Math.round((float)Math.sqrt(deltaY*deltaY+deltaZ*deltaZ));
				}
				// special case: if lift is already on right position
				else if(Math.abs(fromX - toX) == 1 && Math.abs(fromY - toY) == 0){
					return 0;
				}
				else{
					throw new ResourceException("Calulate route error. Not possible buffer at position: "+to.getPosition());
				}
			}
			// lift -> rack resource
			else if(this.containsRackResource(to)){
				// lift -> rack resource  (store load in rack)
				if(Math.abs(fromX - toX) == 1 && Math.abs(fromY - toY) == 0 && Math.abs(fromZ - toZ) == 0 && this.load.getState() == Load.STATE.IN){
					return 1; // 0 because lift has not to move -> no agenda entry
				}
				// lift -> lift (waiting, no movement)
				else if(Math.abs(fromX - toX) == 1 && Math.abs(fromY - toY) == 0 && Math.abs(fromZ - toZ) == 0 && this.load.getState() == Load.STATE.OUT){
					return 0; // 0 because lift has not to move -> no agenda entry
				}
				// lift -> rack resource  (lift -> lift, pythagoras)
				else{
					deltaY = toY - fromY;
					deltaZ = toZ - fromZ;
					return Math.round((float)Math.sqrt(deltaY*deltaY+deltaZ*deltaZ));
				}
				
			}
			else{
				throw new ResourceException("Calulate route error. (to) destination: "+to.getPosition()+" does not belong to this lift:"+from.getPosition());
			}
		}
		
		/**
		 * from a rack resource to any resource
		 */	
		else if(this.containsRackResource(from)){
			// Rack resource -> lift
			if(to instanceof Lift){
				if(Math.abs(fromX - toX) == 1){
					return 1;
				}
				else{
					throw new ResourceException("Not possible lift"+" \nfrom:"+from+" \nto"+to);
				}
			}
			// Only to calculate the lift route from   next to rack resource position   to   next to output buffer position
			else if(to instanceof Buffer){
				deltaY = toY - fromY;
				deltaZ = toZ - fromZ;
				return Math.round((float)Math.sqrt(deltaY*deltaY+deltaZ*deltaZ));
			}
		}
		throw new ResourceException("Calulate route error. Not supported query\nfrom: "+from.toString()+"\nto: "+to.toString());
	}
	
	
	/**
	 * Calculates all available conveyor resources
	 * 
	 * @param from - start resource of the search process
	 * @param offsetTime - offset time between a load after another
	 * @throws ResourceException Resource not found
	 */
	protected void calculateAvailableConveyorResources(Buffer from, int offsetTime) throws ResourceException {
		// Create lists
		this.availableConveyorResources = new ArrayList<Resource>();
		// Initialize variables
		int route = 0, lastAgendaEntryEndTime = 0, startTime = 0;
		boolean availableResources = false;
		
		if(from.getType() == Buffer.Type.IN){
			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {
				// Calculate route from resource to resource
				route = this.calculateRouteFromTo(from, conveyorResource);
				// Get last agenda entry end time
				lastAgendaEntryEndTime = conveyorResource.getAgenda().getLastEntry().getEndTime();
				// Calculate start time of resource
				startTime = this.systemEntryTime + this.CONVEYOR_SPEED*route;
				
				if(startTime >= lastAgendaEntryEndTime){
					// Add entries to array lists
					this.availableConveyorResources.add(conveyorResource);
				}
				// Break if there is a conflict
				else{
					break;
				}
			}
		}else if (from.getType() == Buffer.Type.OUT){
			
			int counter = 0;
			int counterFalse = 0;
			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {
				//for output no need to check 
				if(conveyorResource.getPosition().getY() == (4+this.RACK_LENGTH)/2-1){
					continue;
				}
				// Calculate route from resource to resource
				route = this.calculateRouteFromTo(from, conveyorResource);
				// Get last agenda entry end time
				lastAgendaEntryEndTime = conveyorResource.getAgenda().getLastEntry().getEndTime();
				// Calculate start time of resource
				startTime = this.systemEntryTime + this.CONVEYOR_SPEED*route + offsetTime;
				if(Math.abs(from.getPosition().getY() - conveyorResource.getPosition().getY()) == 1 && from.getPosition().getX() == conveyorResource.getPosition().getX()){
					availableResources = true;
					counterFalse = counter;
					
				}
				else{
					counter++;
				}
				
				if(availableResources && startTime >= lastAgendaEntryEndTime){
					// Add entries to array lists
					this.availableConveyorResources.add(conveyorResource);
				}
			}
			int totalSize = this.conveyor.getResources().size()-1;
			if(!(totalSize - counterFalse == this.availableConveyorResources.size())){
				this.availableConveyorResources.clear();
			}
		}
		
	}
	
	
	/**
	 * Get available conveyor resources
	 *
	 * @param buffer
	 */
	protected void getAvailableConveyorResources(Buffer buffer) {
		// Create lists
		this.availableConveyorResources = new ArrayList<Resource>();
		int bufferX = buffer.getPosition().getX();
		int bufferY = buffer.getPosition().getY();
		
		if(buffer.getType() == Buffer.Type.IN){
			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {
				int conX = conveyorResource.getPosition().getX();
				int conY = conveyorResource.getPosition().getY();

				if(conX == 1){
					this.availableConveyorResources.add(conveyorResource);
				}
				// Upper part
				else if(conY < (4+this.RACK_LENGTH)/2){
					if(bufferX > conX){
						this.availableConveyorResources.add(conveyorResource);
					}
					// Special case conveyor resource next to top right corner
					else if(conX == bufferX){
						this.availableConveyorResources.add(conveyorResource);
					}
					else{
						break;
					}
				}
				// Lower part
				else{
					if(bufferX < conX){
						this.availableConveyorResources.add(conveyorResource);
					}
					else{
						break;
					}
				}
			}
		}
		
		else if (buffer.getType() == Buffer.Type.OUT){
			boolean hasFoundStartResource = false;

			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {
				int conX = conveyorResource.getPosition().getX();
				int conY = conveyorResource.getPosition().getY();
				
				if(Math.abs(bufferX -conX) == 0 && Math.abs(bufferY-conY) == 1){
					hasFoundStartResource = true;
				}
				if(hasFoundStartResource){
					this.availableConveyorResources.add(conveyorResource);
				}
			}
		}
	}
	
	
	/**
	 * Calcuates if there are any conflicts on the conveyor when booking in
	 * 
	 * @param to
	 * @return the boolean value if there are conflicts on the conveyor
	 * @throws ResourceException
	 */
	protected boolean hasBookInConveyorConflict(Buffer to) throws ResourceException{
		// Initialize variables
		int route = 0, timeToResource = 0, timeLeaveResource = 0;
		
		if(to.getType() == Buffer.Type.IN){
			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {

				// Calculate route from resource to resource
				route = this.calculateRouteFromTo(this.entryBuffer, conveyorResource);
				// Get last agenda entry end time
				// Calculate start time of resource
				
				timeToResource = this.systemEntryTime + this.CONVEYOR_SPEED*route;
				timeLeaveResource = timeToResource + 1*this.CONVEYOR_SPEED;
				
				AgendaEntry aeBefore = conveyorResource.getAgenda().getClosestEntryBefore(timeLeaveResource);
				AgendaEntry aeAfter = conveyorResource.getAgenda().getClosestEntryAfter(timeLeaveResource);

				// No entries
				if(aeBefore == null && aeAfter == null){
					continue;
				}
				// Only one entry
				else if(aeBefore == null && aeAfter != null){
					if(aeAfter.getEndTime() <= timeLeaveResource){
						return true;
					}
				}
				// aeBefore is the last entry
				else if(aeBefore != null && aeAfter == null){
					if(aeBefore.getEndTime() >= timeToResource){
						return true;
					}
				}
				else if(aeBefore.getEndTime() >= timeToResource || timeLeaveResource >= aeAfter.getStartTime()){
					return true;
				}
				
			}
		}
		else{
			throw new ResourceException("hasBookInConveyorConflict got no input buffer");
		}
		
		return false;
	}

	
	/**
	 * Calcuates if there are any conflicts on the conveyor when booking out
	 * 
	 * @param from
	 * @param offsetTimeToOutputBuffer
	 * @return the boolean value if there are conflicts on the conveyor
	 * @throws ResourceException
	 */
	protected boolean hasBookOutConveyorConflict(Buffer from, int offsetTimeToOutputBuffer) throws ResourceException{
		// Initialize variables
		int route = 0;
		
		if (from.getType() == Buffer.Type.OUT){
			boolean hasFoundStartResource = false;
			// Collect all available resources
			for (Resource conveyorResource : this.conveyor.getResources()) {
				int fromX = from.getPosition().getX();
				int fromY = from.getPosition().getY();
				int conX = conveyorResource.getPosition().getX();
				int conY = conveyorResource.getPosition().getY();
				
				if(Math.abs(fromX-conX) == 0 && Math.abs(fromY-conY) == 1){
					hasFoundStartResource = true;
				}
				if(hasFoundStartResource){
					// Calculate route from resource to resource
					route = this.calculateRouteFromTo(from, conveyorResource);
					// Calculate start time of resource
					int timeToResource = offsetTimeToOutputBuffer + route*this.CONVEYOR_SPEED;
					int timeLeaveResource = timeToResource + 1*this.CONVEYOR_SPEED;
					
					AgendaEntry aeBefore = conveyorResource.getAgenda().getClosestEntryBefore(timeLeaveResource);
					AgendaEntry aeAfter = conveyorResource.getAgenda().getClosestEntryAfter(timeLeaveResource);

					// No entries
					if(aeBefore == null && aeAfter == null){
						return false;
					}
					// Only one entry
					else if(aeBefore == null && aeAfter != null){
						if(aeAfter.getEndTime() <= timeLeaveResource){
							return true;
						}
					}
					// aeBefore is the last entry
					else if(aeBefore != null && aeAfter == null){
						if(aeBefore.getEndTime() >= timeToResource){
							return true;
						}
					}
					else if(aeBefore.getEndTime() >= timeToResource || timeLeaveResource >= aeAfter.getStartTime()){
						return true;
					}
				}
			}
		}
		else{
			throw new ResourceException("hasBookOutConveyorConflict got no output buffer");
		}
		return false;
	}
	
	
	/**
	 * Calculates all available buffers without the entry buffer
	 * 
	 * @param from - start resource of the search process
	 * @throws ResourceException Resource not found
	 */
	protected void calculateAvailableInputBuffers(Resource from) throws ResourceException {
		// Create lists
		this.availableInputBuffers = new ArrayList<Buffer>();
		// Initialize x and y values
		int conveyorResourceX = 0, conveyorResourceY = 0, bufferX = 0, bufferY = 0;
		// Collect all available resources
		for(Buffer buffer: this.buffers){
			// If buffer is an input buffer
			if(buffer.getType().equals(Buffer.Type.IN)){
				// Add for all available conveyor resources the corresponding buffer to the list of available buffers
				for(Resource conveyorResource : this.availableConveyorResources){
					// Calculate x and y values
					conveyorResourceX = conveyorResource.getPosition().getX();
					conveyorResourceY = conveyorResource.getPosition().getY();
					bufferX = buffer.getPosition().getX();
					bufferY = buffer.getPosition().getY();
					// Check if the position of the buffer belongs to the conveyor resource
					if((Math.abs(conveyorResourceY - bufferY) == 1 && Math.abs(conveyorResourceX - bufferX) == 2) || conveyorResourceX - bufferX == 1){
						// Calculate route to given buffer
						int route = this.calculateRouteFromTo(from, buffer);
						// Get last agenda entry end time
						int lastAgendaEntryEndTime = buffer.getAgenda().getLastEntry().getEndTime();
						// Calculate start time of resource
						int startTime = systemEntryTime+CONVEYOR_SPEED*route;
						
						if(startTime >= lastAgendaEntryEndTime && buffer.getPosition().getX() > 0){
							// Add entries to array lists
							this.availableInputBuffers.add(buffer);
							// Break loop of conveyor resources
							break;
						}
					}
				}
			}
		}
	}
	

	/**
	 * Searches if one of the racks contains the given resource
	 * 
	 * @param resource - a rack resource
	 * @return true if found
	 */
	private boolean containsRackResource(Resource resource){
		for (Rack rack : this.racks) {
			if(rack.contains(resource)){
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * Gets rack resource of a given load
	 * 
	 * @param load to search
	 * @return rack resource with this load
	 */
	protected Resource getRackResourceOfLoad(Load load){
		Resource rackResource = null;
		for (Rack rack : this.racks) {
			rackResource = rack.getResourceOfLoad(load);
			if(rackResource !=null){
				return rackResource;
			}
		}
		return null;
	}
	
	
	/**
	 * Books the load route
	 * 
	 * @param load to book
	 */
	public void bookLoadRoute(Load load) throws ResourceException{
		List<AgendaEntry> entries = new ArrayList<AgendaEntry>();
		List<AgendaEntry> bufferEntries = new ArrayList<AgendaEntry>();
		List<AgendaEntry> sortedBufferEntries = new ArrayList<AgendaEntry>();
		
		// buffers
		for (Buffer b : this.buffers) {
			for(AgendaEntry ae :b.getAgenda().getAgendaEntries()){
				if(ae.getLoad().equals(load)){
					// One position before buffer (upper part of system)
					if(b.getPosition().getX() > 0 && b.getPosition().getY() == 1){
						bufferEntries.add(ae);
					}
					// One position before buffer (lower part of system)
					else if(b.getPosition().getY() > (4+this.RACK_LENGTH)/2){
						bufferEntries.add(ae);
					}
					entries.add(ae);
				}
			}
		}
		
		// conveyor resources
		for(Resource r: this.conveyor.getResources()){
			for(AgendaEntry ae :r.getAgenda().getAgendaEntries()){
				if(load.equals(ae.getLoad())){
					entries.add(ae);
				}
			}
		}
		
		// lift
		for(AgendaEntry ae:this.lift.getAgenda().getAgendaEntries()){
			if(ae.getLoad() != null && ae.getLoad().equals(load)){
				entries.add(ae);
			}
		}
		
		// rack resource
		if(load.getState() == Load.STATE.OUT){
			load.releaseLoad(this.systemEntryTime);
		}
		for (Rack rack : this.racks) {
			Resource resource = rack.getResourceOfLoad(load);
			if(resource != null){
				for(AgendaEntry ae : resource.getAgenda().getAgendaEntries()){
					if(ae.getLoad().equals(load)){
						entries.add(ae);
					}
				}
			}
		}
		
		// corner positions
		if(load.getState() == Load.STATE.IN){
			for(int i=0; i<this.conveyor.getCornerPositions().size()-1; i++){
				Position cornerPosition = this.conveyor.getCornerPositions().get(i);
				Resource corner = new Resource(cornerPosition);
				int route = this.calculateRouteFromTo(this.entryBuffer, corner);
				int startTime = this.systemEntryTime + route*this.CONVEYOR_SPEED;
				route = this.calculateRouteFromTo(corner, corner);
				int endTime = startTime + route*this.CONVEYOR_SPEED;
				AgendaEntry cornerAE = new AgendaEntry(startTime, cornerPosition, endTime, cornerPosition, load);
				entries.add(cornerAE);
				if(this.inputBuffer.getPosition().getY() < (4+this.RACK_LENGTH)/2){
					break;
				}
			}
		}
		else{
			for(int i=this.conveyor.getCornerPositions().size()-1; i>0; i--){
				Position cornerPosition = this.conveyor.getCornerPositions().get(i);
				Resource corner = new Resource(cornerPosition);
				int route = this.calculateRouteFromTo(this.outputBuffer, corner);
				int startTime = this.outputBuffer.getAgenda().getLastEntry().getStartTime() + route*this.CONVEYOR_SPEED;
				route = this.calculateRouteFromTo(corner, corner);
				int endTime = startTime + route*this.CONVEYOR_SPEED;
				AgendaEntry cornerAE = new AgendaEntry(startTime, cornerPosition, endTime, cornerPosition, load);
				entries.add(cornerAE);
				if(this.outputBuffer.getPosition().getY() > (4+this.RACK_LENGTH)/2){
					break;
				}
			}
		}
		
		// position before buffer
		// for input loads
		if(load.getState() == Load.STATE.IN){
			Collections.sort(bufferEntries);
			for(int j = 0; j < bufferEntries.size(); j++){
				if (j<bufferEntries.size()-1){
					if(bufferEntries.get(j).getEndTime() == bufferEntries.get(j+1).getEndTime()){
						if(bufferEntries.get(j).getStartTime() < bufferEntries.get(j+1).getStartTime()){
							sortedBufferEntries.add(bufferEntries.get(j));
						} else {
							sortedBufferEntries.add(bufferEntries.get(j+1));
							sortedBufferEntries.add(bufferEntries.get(j));
							j++;
						}
					} else {
						sortedBufferEntries.add(bufferEntries.get(j));
					}
				} else {
					sortedBufferEntries.add(bufferEntries.get(j));
				}
			}
			AgendaEntry tempAE = sortedBufferEntries.get(0);
			// position before input buffer
			int startTime = tempAE.getStartTime()-1*this.CONVEYOR_SPEED;
			int endTime = tempAE.getStartTime();
			// upper part of system
			if(this.inputBuffer.getPosition().getY() == 1){
				Position position = new Position(this.inputBuffer.getPosition().getX(), this.inputBuffer.getPosition().getY()-1, this.inputBuffer.getPosition().getZ());
				entries.add(new AgendaEntry(startTime, position, endTime, position, load));
			}
			// lower part of system
			else{
				Position position = new Position(this.inputBuffer.getPosition().getX(), this.inputBuffer.getPosition().getY()+1, this.inputBuffer.getPosition().getZ());
				entries.add(new AgendaEntry(startTime, position, endTime, position, load));
			}
		}
		// For output loads
		else{
			int route = this.calculateRouteFromTo(this.outputBuffer, this.exitBuffer);
			int startTime = this.outputBuffer.getAgenda().getLastEntry().getStartTime() + route*this.CONVEYOR_SPEED - 1*this.CONVEYOR_SPEED;
			int endTime = startTime + 1*this.CONVEYOR_SPEED;
			Position position = new Position(this.exitBuffer.getPosition().getX()+1, this.exitBuffer.getPosition().getY(), this.exitBuffer.getPosition().getZ());
			entries.add(new AgendaEntry(startTime, position, endTime, position, load));
		}
		
		// sort by end time
		Collections.sort(entries);
		
		// sort agenda entries by start time
		load.getAgenda().sortAgendaEntriesByStartTime();
		
		// add entries
		for (AgendaEntry ae : entries) {
			if(!load.getAgenda().getAgendaEntries().contains(ae)){
				load.getAgenda().addEntry(ae);
			}
		}
		
		System.out.println("\nLoad route: ");
		for (AgendaEntry ae : load.getAgenda().getAgendaEntries()) {
			System.out.println(ae);
		}
		
		this.cleanAllSelectedResources();
	}
	
	
	/**
	 * Cleans all used instance fields
	 */
	private void cleanAllSelectedResources(){
		this.inputBuffer = null;
		this.outputBuffer = null;
		this.lift = null;
		this.rackResource = null;
	}
	
}
